#include "generalPrototype.hpp"
#include "../../Libs/SimpleJSON/JSON.hpp"
#include "../../MiscUtils/TdrMisc/SEngine.h"
#include <Windows.h>
//#include "SEngine/SimpleJSON/JSON.hpp"
//#include "SEngine/TdrMiscHeaders/Auxi.hpp"
#include <iostream>

// https://github.com/nbsdx/SimpleJSON

using namespace std;

int ParaMapSlave::has(std::wstring key) {
	return paraMap.find(key) != paraMap.end();
}

int ParaMapSlave::getInt(std::wstring key) {
	return TWFormater::str2int(paraMap[key]);
}

int ParaMapSlave::getFloat(std::wstring key) {
	return TWFormater::str2float(paraMap[key]);
}

float ParaMapSlave::extractConfigSpeed(std::wstring configStr) {
	float speed = 0;
	if (configStr.length() > 0) {
		if (configStr.compare(L"{}") == 0) {

		}
		// zone paras: set in AbActTranslator
		auto paras = TFormater::split(configStr, L';');
		for (auto para : paras) {
			// see AbActTranslator::pick_up_tip, and AbActTranslator::align
			if (TWFormater::beginWith(para, L"with_speed")) {
				para = para.substr(sizeof("with_speed"));
			}
			// see AutomaProfile::makeTemplateLiquidHandler
			if (has(para)) {
				speed = getFloat(para);
			}
			else {
				TLogger::writeWarnning(L"speed " + para + L" required but not found");
				speed = -1;
			}
		}
	}
	return speed;
}

GeneralSlave::GeneralSlave() {
	genSlaveInit();
}

GeneralSlave::~GeneralSlave() {
	genSlaveRelease();
}

void GeneralSlave::genSlaveInit() {
	_commCB.callbackIndex = _commCB.addMeToArray(this);

	if (_comm.embodiment != nullptr) {
		_comm.embodiment->addAvatar(this);
	}
}

void GeneralSlave::genSlaveInit(GeneralSlave* ref) {
	_meta = ref->_meta;
	_comm = ref->_comm;
	_callbacks = ref->_callbacks;

	_logger.logFilenameRef = &_generalStat.logFilename;
	genSlaveInit();
}

void GeneralSlave::genSlaveCopy(GeneralSlave* ref) {
	_meta = ref->_meta;
	_comm = ref->_comm;
	_callbacks = ref->_callbacks;

	_logger.logFilenameRef = &_generalStat.logFilename;
}

void GeneralSlave::genSlaveRelease() {
	_commCB.removeMeFromArray(_commCB.callbackIndex);
}

void GeneralSlave::setNewLogFile(std::wstring filename, bool flagErase) {
	_generalStat.logFilename = filename;

	if (filename.length() > 0) {
		TFile::makeDirTreeIfNeeded(filename);

		if (flagErase) {
			TWrite2Txt::clearFile(filename);
		}
	}
}

int GeneralSlave::setNewFriendlyName(std::wstring name) {
	_meta.friendlyName = name;
	_callbacks.nameInCB = name;
	return _meta.friendlyName.length();
}

void GeneralSlave::setNewReferenceName(std::wstring name) {
	_meta.referenceName = name;
}

void GeneralSlave::setGlobalName(std::wstring name) {
	_meta.globalName = name;
}

void GeneralSlave::setDefaultFriendlyName() {
	_meta.friendlyName = _meta.referenceName;
	_callbacks.nameInCB = _meta.referenceName;
}

std::wstring GeneralSlave::getReferenceName() {
	if (_meta.partOfComboSlave.length() > 0) {
		return _meta.partOfComboSlave + L"." + _meta.referenceName;
	}
	return _meta.referenceName;
}

std::wstring GeneralSlave::getGlobalName() {
	return _meta.globalName;
}

std::wstring GeneralSlave::getFriendlyName() {
	return _meta.friendlyName;
}

std::wstring GeneralSlave::getMetaByName(std::wstring name) {
	if (name.compare(L"descriptor.identifier") == 0) {
		return _comm.identifier;
	}
	return L"NA";
}

std::wstring GeneralSlave::setMetaByName(std::wstring name, std::wstring val) {
	if (name.compare(L"descriptor.identifier") == 0) {
		_comm.identifier = val;
		return _comm.identifier;
	}
	return L"NA";
}

void GeneralSlave::setNewRecordFile(std::wstring filename) {
	int stop = 1;
}

int GeneralSlave::updateOnConnectionBroken() {
	_comm.markBroken();
	return true;
}

int GeneralSlave::softDisConnect() {
	_comm._conStat = ConnectionStatus::Missing;
	_generalStat._exeStat = ExecutionStatus::NotAvailable;

	for (auto avt : _comm.avatars) {
		avt->_comm._conStat = _comm._conStat;
		avt->_generalStat._exeStat = _generalStat._exeStat;
	}

	return 1;
}

int GeneralSlave::hardDisConnect() {
	if (_meta.isSimulated > 0) {
		return simuDisConnect();
	}
	writeLogLinePlain(L"Disconnect " + getReferenceName());

	_comm._conStat = ConnectionStatus::Missing;
	_generalStat._exeStat = ExecutionStatus::NotAvailable;

	if (_comm.commHandle == nullptr) {
		return 1;
	}
	if (_comm.isCommChOwner <= 0) {
		_comm.commHandle = nullptr;
		return 1;
	}
	_comm.commHandle->disconnect();
	delete _comm.commHandle;
	_comm.commHandle = nullptr;

	//_meta.isExpecting = false;

	return 1;
}

int GeneralSlave::connectThroughUSB(int discoverMode) {
	HandShakeProtocol handshakes =
		makeGeneralSerialHandshake(
			_comm.handShakePreSends, _comm.handShakePreReceives,
			_meta.model, _comm.identifier, discoverMode);
	vector<wstring> hwids;
	if (_comm.appearance.length() > 0) {
		hwids.push_back(_comm.appearance);
	}
	else {
		hwids = HardwareList::getSerialHWIDs();
	}

	if (isSimu()) {
		if (connectThroughUSBSimu(handshakes, hwids, discoverMode) > 0) {
			updateAfterConnectAny(nullptr);
			return 1;
		}
		return 0;
	}

	int cnnState = connectThroughUSBExe(handshakes, hwids, discoverMode);
	if (cnnState > 0) {
		updateAfterConnectAny(&handshakes);
		return 1;
	}
	else {
		return cnnState;
	}
}

int GeneralSlave::connectThroughTBridge(int discoverMode) {
	if (_comm.coordinator == nullptr) {
		// this must be a coordinator
		// should call through overwriten method
		// TAutomatonFrameCoordinator::connectThroughTBridge
		TLogger::writeLine("called connectThroughTBridge with null coordinator in a GeneralSlave");

		return 0;
	}

	if (_comm.coordinator->isConnected() == 0) {
		return 0;
	}

	HandShakeProtocol handshakes =
		makeGeneralSerialHandshake(
			_comm.handShakePreSends, _comm.handShakePreReceives,
			_meta.model, _comm.identifier, discoverMode);

	// for coordinators, see
	// #TAutomatonFrameCoordinator::connectThroughTBridgeNonRoot
	int cnnState = connectThroughTBridgeNonRoot(handshakes, discoverMode);
	if (cnnState > 0) {
		updateAfterConnectAny(&handshakes);
		return 1;
	}
	else {
		return cnnState;
	}
}

bool GeneralSlave::shareLink(GeneralSlave* reference) {
	if (reference == nullptr) {
		return false;
	}
	GeneralSlave* rootCrdr = reference;
	while (rootCrdr->_comm.coordinator != nullptr) {
		rootCrdr = rootCrdr->_comm.coordinator;
	}
	_comm.commHandle = rootCrdr->_comm.commHandle;
	_comm.isCommChOwner = 0;
	_comm.connectionHardware = rootCrdr->_comm.connectionHardware;

	for (auto avatar : _comm.avatars) {
		avatar->shareLink(rootCrdr);
	}
	// no need to update embodiment: always connect through the embodiment interface as in logic in GeneralSlave::connect

	return true;

	//if (reference->_comm.isCommChOwner == 1) {
	//	TLogger::writeLine("Sharing link of owner not implemented");
	//	return false;
	//	if (reference->_comm.commHandle->getInterfaceType().compare("serial") == 0) {
	//		return false;
	//		//auto channel = new CommChannelSerial();
	//		//channel->shareManager((CommChannelSerial*)reference->_comm.commHandle);
	//		//_comm.commHandle = channel;
	//		//_comm.commHandle.
	//	}
	//}
	//else {
	//	// very much like GeneralSlave::connectThroughTBridgeNonRoot and GeneralSlave::updateAfterConnectThroughBridge 

	//	GeneralSlave* rootCrdr = reference->_comm.coordinator;
	//	while (rootCrdr->_comm.coordinator != nullptr) {
	//		rootCrdr = rootCrdr->_comm.coordinator;
	//	}

	//	writeLogLinePlain(_meta.referenceName + L":" + L" Connected through shared bridge");
	//	writeLogLinePlain(L"Root: " + rootCrdr->_meta.referenceName);
	//	_comm._conStat = ConnectionStatus::Connected;
	//	_meta._exeStat = ExecutionStatus::Activated;
	//	_comm.commHandle = rootCrdr->_comm.commHandle;
	//	_comm.isCommChOwner = 0;
	//	_comm.coordinatorIndex = reference->_comm.coordinatorIndex;
	//	_comm.connectionHardware = rootCrdr->_comm.connectionHardware;
	//}
	//return false;
}

void GeneralSlave::addAvatar(GeneralSlave* slave) {
	for (auto& avt : _comm.avatars) {
		if (avt == nullptr) {
			avt = slave;
			return;
		}
	}
	_comm.avatars.push_back(slave);
}

int GeneralSlave::simuDisConnect() {
	logLineGlobal("Simu Donnected");
	writeLogLinePlain("Simu disconnect");
	_comm._conStat = ConnectionStatus::Missing;
	_generalStat._exeStat = ExecutionStatus::Activated;

	_meta.isExpecting = false;
	return 1;
}

// bookmark: test
int GeneralSlave::updateConnectFromEmbodiment() {
	if (isConnected() > 0) {
		return 1;
	}
	if (_comm.embodiment == nullptr) {
		return 0;
	}
	if (_comm.embodiment->isConnected() <= 0) {
		return -1;
	}
	if (_meta.isExpecting) {
		_comm.commHandle = CommChannel::shallowCopy(_comm.embodiment->_comm.commHandle);
		_comm.commHandle->setMPCB(_commCB.callbackIndex,
			_callbacks.serialSentCBWrap,
			_callbacks.serialReceiveCBWrap);

		_meta.isSimulated = _comm.embodiment->_meta.isSimulated;

		return 2;
	}
	return 0;
}

std::wstring GeneralSlave::getConnectionStatStr() {
	if (!shouldConnect()) {
		return L"Ignored";
	}
	if (_comm._conStat == ConnectionStatus::Missing) {
		return L"Missing";
	}
	else if (_comm._conStat == ConnectionStatus::Detected) {
		return L"Detected";
	}
	else if (_comm._conStat == ConnectionStatus::Connected) {
		return L"Connected";
	}
	else if (_comm._conStat == ConnectionStatus::Broken) {
		return L"Broken";
	}
	else {
		return L"Undefined";
	}
}

HandShakeProtocol GeneralSlave::makeGeneralSerialHandshake(std::wstring model, std::wstring identifier, int discoverMode) {
	vector<string> handShakePreSends, handShakePreReplys;

	HandShakeProtocol handshakes;
	makeGeneralSerialHandshakeCore(handshakes, handShakePreSends, handShakePreReplys, model, identifier, discoverMode);
	return handshakes;
}

HandShakeProtocol GeneralSlave::makeGeneralSerialHandshake(vector<string> handShakePreSends, vector<string> handShakePreReplys, std::wstring model, std::wstring identifier, int discoverMode) {
	HandShakeProtocol handshakes;
	makeGeneralSerialHandshakeCore(handshakes, handShakePreSends, handShakePreReplys, model, identifier, discoverMode);
	return handshakes;
}

// see also 
int GeneralSlave::connect(int discoverMode) {
	if (_comm.embodiment != nullptr) {
		int ret = _comm.embodiment->connect(discoverMode);
		//_comm.commHandle
		return ret;
	}
	if (isConnected()) {
		return 2;
	}
	if (_meta.isExpecting) {
		TLogger::writeLine(L"Attempt to connect " + _meta.referenceName);
		//if (_meta.isSimulated > 0) {
		// implement simulated connect
		//	return simuConnectUpdate();
		//}

		if (_meta.platform.compare(L"Terrydr_bridge") == 0) {
			TLogger::writeLine("Bridge");
			if (preSendingConfig(_meta.friendlyName, _comm.coordinatorIndex, 0) > 0) {
				// see also TAutomatonFrameCoordinator::connectThroughTBridge
				return connectThroughTBridge(discoverMode);
			}
			else {
				TLogger::writeLine("Wait for coordinator to connect first");
				return -1;
			}
		}
		else if (_comm.connectionType.compare(L"USB") == 0 || _comm.connectionType.compare(L"usb") == 0) {
			return connectThroughUSB(discoverMode);
		}
		return -1;
	}
	return 1;
}

int GeneralSlave::confirmConnect() {
	if (_comm.embodiment != nullptr) {
		return _comm.embodiment->confirmConnect();
	}
	if (isConnected() <= 0) {
		return 0;
	}

	if (_comm.coordinator != nullptr) {
		if (_comm.coordinator->isConnected() <= 0) {
			updateOnConnectionBroken();
			return 0;
		}
	}
	if (preSendingConfig(_meta.friendlyName, -1) <= 0) {
		updateOnConnectionBroken();
		return 0;
	}

	if (reConnect() > 0) {
		return 1;
	}

	updateOnConnectionBroken();
	return 0;
}

int GeneralSlave::reConnect() {
	if (_comm.embodiment != nullptr) {
		return _comm.embodiment->reConnect();
	}
	if (preSendingConfig() < 0) {
		return -1;
	}

	// see GeneralSlave::connectThroughTBridgeNonRoot
	HandShakeProtocol handshakes =
		makeGeneralSerialHandshake(
			_meta.model, _comm.identifier, 0);

	CommChannel* rootChannel;
	if (_comm.coordinator == nullptr) {
		rootChannel = _comm.commHandle;
	}
	else {
		GeneralSlave* rootCrdr = _comm.coordinator;
		while (rootCrdr->_comm.coordinator != nullptr) {
			rootCrdr = rootCrdr->_comm.coordinator;
		}
		rootChannel = rootCrdr->_comm.commHandle;
	}

	if (rootChannel == nullptr) {
		return -1;
	}
	// see
	// CommChannelSerial::executeHandshake
	if (rootChannel->executeHandshake(handshakes, _comm.SOFT_TIMEOUT_TBRIDGE) > 0) {
		// nothing changed
		if (_comm._conStat != ConnectionStatus::Connected) {
			_comm.markRecovery();
			resetBufferedVolatileVariables();
		}
		return 1;
	}
	//if (_comm._conStat != ConnectionStatus::Broken) {
	forceRebuildBridges();
	//}
	return 0;
}

int GeneralSlave::simuConnectUpdate() {
	if (_comm.embodiment != nullptr) {
		return _comm._conStat == GeneralSlave::ConnectionStatus::Connected;
	}
	if (_comm._conStat == GeneralSlave::ConnectionStatus::Connected) {
		return 1;
	}
	if (_comm.coordinator != nullptr) {
		if (_comm.coordinator->isConnected() <= 0) {
			return 0;
		}
	}
	if (_comm._conStat < GeneralSlave::ConnectionStatus::Detected) {
		_comm._conStat = GeneralSlave::ConnectionStatus::Detected;
		_comm.connectionSubType = L"simulated";
		logLineGlobal("Simu state -> Detected");
		return 0;
	}
	else {
		_comm._conStat = GeneralSlave::ConnectionStatus::Connected;
		_comm.connectionSubType = L"simulated";
		updateAfterConnectAny(nullptr);
		logLineGlobal("Simu state -> Connected");
		return 1;
	}
}

int GeneralSlave::connectThroughUSBExe(HandShakeProtocol& protocol, std::vector<std::wstring> hwids, int discoverMode) {
	void* manager = CommsInterface::searchForSlavesOverSerial(
		isSimu(),
		hwids, 115200, 'n', 8, 1, protocol);
	if (manager != nullptr) {
		updateBeforeConnectToUsbSerial(manager);
		if (discoverMode == 1) {
			TLogger::writeLine(L"Discovered new " + getModel() + L" through " + getFullConnectionType());
		}
		return 1;
	}
	return -1;
}

int GeneralSlave::connectThroughUSBSimu(HandShakeProtocol& protocol, std::vector<std::wstring> hwids, int discoverMode) {
	writeLogLinePlain(_meta.referenceName + L":" + L" Simu connect through USB serial");
	writeLogLinePlain(L"Expected hwids: ");
	for (auto& id : hwids) {
		writeLogLinePlain(L"  " + id);
	}

	return connectThroughUSBExe(protocol, hwids, discoverMode);
}

void GeneralSlave::updateBeforeConnectToUsbSerial(void* serialPortManager) {
	writeLogLinePlain(_meta.referenceName + L":" + L" Connected through USB serial");
	//writeLogLinePlain("Connected through USB serial");
	//_comm._conStat = ConnectionStatus::Connected;
	_generalStat._exeStat = ExecutionStatus::Activated;
	_comm.commHandle = new CommChannelSerial();
	_comm.commHandle->setManager(serialPortManager);
	_comm.commHandle->setMPCB(_commCB.callbackIndex, _callbacks.serialSentCBWrap, _callbacks.serialReceiveCBWrap);
	_comm.connectionHardware = TFormater::string2wstring(_comm.commHandle->getHardware());

	writeLogLinePlain("  Detail: " + _comm.commHandle->toStr());
}

void GeneralSlave::updateBeforeConnectThroughBridge(GeneralSlave* bridgeRoot) {
	writeLogLinePlain(_meta.referenceName + L":" + L" Connected through bridge");
	writeLogLinePlain(L"Root: " + bridgeRoot->_meta.referenceName);
	//_comm._conStat = ConnectionStatus::Connected;
	_generalStat._exeStat = ExecutionStatus::Activated;
	shareLink(bridgeRoot);
	//_comm.commHandle = bridgeRoot->_comm.commHandle;
	//_comm.isCommChOwner = 0;
	//_comm.connectionHardware = bridgeRoot->_comm.connectionHardware;
}

void GeneralSlave::updateAfterConnectAny(HandShakeProtocol* protocol) {
	_comm._conStat = ConnectionStatus::Connected;
	resetBufferedVolatileVariables();

	if (protocol != nullptr) {
		if (_comm.identifier.length() == 0) {
			_comm.identifier = TFormater::string2wstring(protocol->getReply("getID"));
			TLogger::writeLine(L"No id was given for " + getReferenceName() + L", choose first discovery: " + _comm.identifier);
		}
	}

	shareLink(_comm.embodiment);

	for (auto avt : _comm.avatars) {
		TLogger::writeLine(L"Avatar connected: " + avt->getReferenceName());
		avt->updateAfterConnectAny(nullptr);
	}
}

int GeneralSlave::forceRebuildBridges() {
	if (_comm.coordinator != nullptr) {
		// coordinator::forceRebuildBridges
		return _comm.coordinator->forceRebuildBridges();
	}
	return 1;
}


std::string GeneralSlave::getTempFlag(std::string key, std::string defaultVal) {
	if (_temp.procFlagMap.find(key) != _temp.procFlagMap.end()) {
		return _temp.procFlagMap[key];
	}
	return defaultVal;
}

std::string GeneralSlave::setTempFlag(std::string key, std::string value) {
	string ret = getTempFlag(key, "");
	_temp.procFlagMap[key] = value;
	return ret;
}

void GeneralSlave::resetTempFlags() {
	_temp.procFlagMap.clear();
}

void GeneralSlave::makeGeneralSerialHandshakeCore(HandShakeProtocol& handshakes,
	std::vector<std::string> handShakePreSends, std::vector <std::string> handShakePreReplys
	, std::wstring model, std::wstring identifier, int discoverMode) {
	if (handShakePreSends.size() > 0) {
		for (int idx = 0; idx < handShakePreSends.size(); idx++) {
			HandShakeProtocol::OneShake preSend("preSend#" + TFormater::int2str(idx));
			preSend.toSend.dataToSend = TFormater::str2u8(handShakePreSends[idx]);
			preSend.exp.set(TFormater::str2u8(handShakePreReplys[idx]), 0, 0, globalSlaveInitializer._comm.defaultTimeout);
			handshakes.handshakes.push_back(preSend);
		}
	}
	//TLogger::writeLine("Skip pre send");

	{
		HandShakeProtocol::OneShake getModel("getModel");
		getModel.toSend.dataToSend = TFormater::str2u8("who\r\n");
		if (model.length() > 0) {
			getModel.exp.set(TFormater::str2u8(TFormater::wstring2string(model) + "\r\n"), 0, 0, globalSlaveInitializer._comm.defaultTimeout);
		}
		else {
			getModel.exp.set(vector<uint8_t>(), 1, 1, globalSlaveInitializer._comm.defaultTimeout);
		}
		handshakes.handshakes.push_back(getModel);
	}
	{
		// UUID
		HandShakeProtocol::OneShake readUUID("readUUID");
		readUUID.toSend.dataToSend.clear();
		readUUID.exp.set(vector<uint8_t>(), 1, 1, globalSlaveInitializer._comm.defaultTimeout);
		handshakes.handshakes.push_back(readUUID);
	}
	{
		HandShakeProtocol::OneShake getID("getID");
		if (discoverMode == 1) {
			getID.toSend.dataToSend = TFormater::str2u8("which\r\n");
			// open reply
			getID.exp.set(vector<uint8_t>(), 1, 1, globalSlaveInitializer._comm.defaultTimeout);
			handshakes.handshakes.push_back(getID);
		}
		else {
			if (identifier.length() == 0) {
				getID.toSend.dataToSend = TFormater::str2u8("which\r\n");
				getID.exp.set(vector<uint8_t>(), 1, 1, globalSlaveInitializer._comm.defaultTimeout);
				handshakes.handshakes.push_back(getID);
			}
			else {
				getID.toSend.dataToSend = TFormater::str2u8("which\r\n");
				getID.exp.set(TWFormater::str2u8(identifier + L"\r\n"), 0, 0, globalSlaveInitializer._comm.defaultTimeout);
				handshakes.handshakes.push_back(getID);
			}
		}
	}
}

int GeneralSlave::connectThroughTBridgeNonRoot(HandShakeProtocol& handshakes, int discoverMode) {
	// see 
	// TAutomatonFrameCoordinator::preSendingConfig
	// TAutomatonFrameCoordinator::connectThroughTBridgeNonRoot
	//if (preSendingConfig(_comm.coordinatorIndex) == 1) {


	//HandShakeProtocol handshakes =
	//	makeGeneralSerialHandshake(
	//		_comm.handShakePreSends, _comm.handShakePreReceives,
	//		_meta.model, _comm.identifier, discoverMode);
	// search for root comm channel
	GeneralSlave* rootCrdr = _comm.coordinator;
	while (rootCrdr->_comm.coordinator != nullptr) {
		rootCrdr = rootCrdr->_comm.coordinator;
	}
	auto* rootChannel = rootCrdr->_comm.commHandle;
	if (rootCrdr->_comm.embodiment != nullptr) {
		rootChannel = rootCrdr->_comm.embodiment->_comm.commHandle;
	}
	// see
	// CommChannelSerial::executeHandshake
	if (rootChannel->executeHandshake(handshakes, _comm.SOFT_TIMEOUT_TBRIDGE) > 0) {
		updateBeforeConnectThroughBridge(rootCrdr);
		//_comm._conStat = ConnectionStatus::Connected;
		return 1;
	}
	//}

	return 0;
}

void GeneralSlave::updateAfterConnectThroughExternalDriver() {
	writeLogLinePlain("Connect to external driver");

	_comm._conStat = ConnectionStatus::Connected;
	_generalStat._exeStat = ExecutionStatus::Activated;
}

std::wstring GeneralSlave::summarizeConnection(int indent) {
	wstring ret;
	wstring indentStr = L"";
	for (int idx = 0; idx < indent; idx++) {
		indentStr += L" ";
	}

	ret += indentStr + L"Slave name: " + _meta.referenceName + L"\n";
	if (_comm.embodiment != nullptr) {
		ret += indentStr + L"  Avatar of: " + _comm.embodiment->getReferenceName() + L"\n";
	}
	ret += indentStr + L"  Log path: " + _generalStat.logFilename + L"\n";
	ret += indentStr + L"  Record path: " + _generalStat.recordFilename + L"\n";

	ret += indentStr + L"  Connect over: " + getFullConnectionType() + L"\n";
	ret += indentStr + L"  Type: " + getTypeWithDetail() + L"\n";
	if (_comm.embodiment == nullptr) {
		ret += indentStr + L"  Model: " + getModel() + L"\n";
		ret += indentStr + L"  ID: " + getID() + L"\n";
		ret += indentStr + L"  Appearance: " + getAppearance() + L"\n";
	}
	wstring statEx;
	if (_meta.isSimulated) {
		statEx = L" (simu)";
	}
	ret += indentStr + L"  Status: " + getConnectionStatStr() + statEx + L"\n";
	if (_comm.coordinator != nullptr) {
		wstring coordLink;
		GeneralSlave* node = _comm.coordinator;
		while (node != nullptr) {
			coordLink += L" <- " + node->getReferenceName();
			node = node->_comm.coordinator;
		}
		ret += indentStr + L"  Coordinating link: " + coordLink + L"\n";
	}

	return ret;
}

int GeneralSlave::queueCommandBase(std::string cmd, int replyLineNum) {
	TLogger::writeLine(TFormater::wstring2string(_meta.referenceName) + " queue command " + cmd);
	U8Vec cmdU8 = TFormater::str2u8(cmd);
	return _rdCmds.add(cmdU8, replyLineNum);
}

int GeneralSlave::queueCommandBase(std::vector<uint8_t> cmd, int replyLineNum) {
	TLogger::writeLine(TFormater::wstring2string(_meta.referenceName) + " queue command " + TFormater::toDisplayable(cmd));
	return _rdCmds.add(cmd, replyLineNum);
}

int GeneralSlave::issueBufferedCommand() {
	auto cmd = _rdCmds.comsume();

	if (cmd.cmd.size() > 0) {
		TLogger::writeLine("Slave " + TFormater::wstring2string(_meta.referenceName) + " issue cmd " + TFormater::toDisplayable(cmd.cmd));
		if (sendU8(cmd.cmd) > 0) {
			if (cmd.replyLineNum > 0) {
				int ret = 1;
				for (int idx = 0; idx < cmd.replyLineNum; idx++) {
					ret = min<int>(ret, receive(nullptr));
				}
				return ret;
			}
			else {
				return 1;
			}
		}
	}
	else {
		TLogger::writeLine("Slave " + TFormater::wstring2string(_meta.referenceName) + " no cmd to issue");
	}
	return 0;
}

void GeneralSlave::enrollInProtocol() {
	TLogger::writeLine(L"Enroll exe " + getReferenceName());
	_generalStat._jobInProtocol = ExecutionStatus::Activated;

	for (auto avt : _comm.avatars) {
		avt->enrollInProtocol();
	}
}

bool GeneralSlave::isEnrolledInProtocol() {
	return _generalStat._jobInProtocol == ExecutionStatus::Activated;
}

void GeneralSlave::dischargeFromProtocol() {
	TLogger::writeLine(L"Discharge " + getReferenceName());
	_generalStat._jobInProtocol = ExecutionStatus::StandBy;

	for (auto avt : _comm.avatars) {
		avt->dischargeFromProtocol();
	}
}

void GeneralSlave::activate() {
	if (_generalStat._exeStat != ExecutionStatus::Activated) {
		TLogger::writeLine(L"Activate " + getReferenceName());
		_generalStat._exeStat = ExecutionStatus::Activated;

		for (auto avt : _comm.avatars) {
			avt->activate();
		}
	}
}

void GeneralSlave::deactivate() {
	wstring hint = (_generalStat._exeStat == ExecutionStatus::StandBy) ? L": set" : L": hold";
	TLogger::writeLine(L"Deact " + getReferenceName() + hint);
	_generalStat._exeStat = ExecutionStatus::StandBy;

	for (auto avt : _comm.avatars) {
		avt->deactivate();
	}
}

bool GeneralSlave::isLoggingDetails() {
	if (_callbacks.isLoggingDetailRef == nullptr) {
		return false;
	}
	return *_callbacks.isLoggingDetailRef;
}

#ifdef _CONSOLE
//#define CONSOLE_PRINT
#endif

void GeneralSlave::Callbacks::serialSentCB(bool success, const void* log, int logLen, const void* data, int dataLen) {
	vector<uint8_t> logV(logLen);
	memcpy(logV.data(), log, logLen);

#ifdef CONSOLE_PRINT
	cout << "[Serial<<" << TStopWatch::getTimeSinceStartAsString(8) << "<<";
	wcout << nameInCB;
	cout << "<<] " << TFormater::toDisplayable(logV) << endl;
#endif

	if (flagForwardToMaster) {
		if (sentCB2Master != NULL) {
			sentCB2Master(nameInCB, idInCB, "", logV);
		}
	}
}

void GeneralSlave::Callbacks::serialReceiveCB(bool success, const void* log, int logLen, const void* data, int dataLen) {
	vector<uint8_t> logV(logLen), datV(dataLen);
	if (logLen > 0) {
		memcpy(logV.data(), log, logLen);
	}
	if (dataLen > 0) {
		memcpy(datV.data(), data, dataLen);
	}

#ifdef CONSOLE_PRINT
	cout << "[Serial>>" << TStopWatch::getTimeSinceStartAsString(8) << "<<";
	wcout << nameInCB;
	cout << "] " << TFormater::toDisplayable(logV) << endl;
	if (datV.size() > 0) {
		cout << "[Serial>?] " << TFormater::toDisplayable(datV) << endl;
	}
#endif

	if (flagForwardToMaster) {
		if (success) {
			if (receiveCB2Master != NULL) {
				receiveCB2Master(nameInCB, idInCB, "", logV, datV);
			}
		}
		else {
			if (timeoutCB2Master != NULL) {
				timeoutCB2Master(nameInCB, idInCB, "", logV, datV);
			}
		}
	}
}

// bookmark: serial: log call
void GeneralSlave::Callbacks::serialSentCBWrap(int index, bool success, const void* log, int logLen, const void* data, int dataLen) {
	CommCallback::allSlaveReps[index]->_callbacks.serialSentCB(success, log, logLen, data, dataLen);
}

void GeneralSlave::Callbacks::serialReceiveCBWrap(int index, bool success, const void* log, int logLen, const void* data, int dataLen) {
	CommCallback::allSlaveReps[index]->_callbacks.serialReceiveCB(success, log, logLen, data, dataLen);
}

void GeneralSlave::Communication::markBroken() {
	_conStat = ConnectionStatus::Broken;

	for (auto avt : avatars) {
		if (avt != nullptr) {
			avt->_comm.markBroken();
		}
	}
}

void GeneralSlave::Communication::markRecovery() {
	_conStat = ConnectionStatus::Connected;

	for (auto avt : avatars) {
		if (avt != nullptr) {
			avt->_comm.markRecovery();
		}
	}
}

GeneralSlave::Communication::~Communication() {
	coordinator = nullptr;
	if (isCommChOwner) {
		delete commHandle;
	}
}

int GeneralSlave::RandomCommandBuffer::add(U8Vec newCmd, int replyLnNum) {
	while (isConsuming) {
		Sleep(1);
	}
	isAdding = true;
	cmdList.push_back(newCmd);
	cmdList.back().replyLineNum = replyLnNum;
	int ret = cmdList.size();
	isAdding = false;
	return ret;
}

GeneralSlave::RandomCommandBuffer::Command GeneralSlave::RandomCommandBuffer::comsume() {
	if (cmdList.size() == 0) {
		return Command();
	}
	while (isAdding) {
		Sleep(1);
	}
	isConsuming = true;

	auto ret = cmdList[0];
	for (int idx = 0; idx < cmdList.size() - 1; idx++) {
		cmdList[idx] = cmdList[idx + 1];
	}
	cmdList.pop_back();

	isConsuming = false;
	return ret;
}

bool GeneralSlave::setSerialLogLocal(bool flag) {
	_callbacks.flagLogLocal = flag;
	return _callbacks.flagLogLocal;
}

bool GeneralSlave::setSerialForward2Master(bool flag) {
	_callbacks.flagForwardToMaster = flag;
	return _callbacks.flagForwardToMaster;
}

void GeneralSlave::logLineGlobal(std::string line) {
	TLogger::writeLine(L"[" + _meta.referenceName + L"]" + TFormater::string2wstring(line));
}

int GeneralSlave::preSendingConfig() {
	for (auto avatar : _comm.avatars) {
		if (avatar->isCoordinator()) {
			// config avatar instead
			// see TAutomatonFrameCoordinator::preSendingConfig(
			// calledIndex=-1,configSelf=1 because is calling this-self
			// callerIsAE=1
			return avatar->preSendingConfig(_meta.friendlyName, -1, -1, 1);
		}
	}

	if (_comm.embodiment != nullptr) {
		return _comm.embodiment->preSendingConfig();
	}

	// auto call the general method or the coordinator ver
	int ret = preSendingConfig(_meta.friendlyName);
	if (ret <= 0) {
		//if (_comm._conStat == ConnectionStatus::Broken) {
		//	TLogger::writeLine("Broken comm detected. Force bridge to rebuild");
		//	int ret = forceRebuildBridges();
		//	if (ret <= 0) {
		//		return ret;
		//	}
		//}
		return ret;
	}

	if (_comm._conStat == ConnectionStatus::Broken) {
		return 0;
	}
	return 1;
}

int GeneralSlave::preSendingConfig(std::wstring baseCallerName, int calledIndex, int configSelf, int callerIsAE) {
	if (_comm.coordinator != nullptr) {
		// implemented in 
		// TAutomatonFrameCoordinator::preSendingConfig
		return _comm.coordinator->preSendingConfig(baseCallerName, _comm.coordinatorIndex);
	}
	return 1;
}

int GeneralSlave::sendString(std::string& cmd) {
	if (_comm.coordinator != nullptr) {
		writeLogLinePlain("B-Send [" + TFormater::toDisplayable(cmd) + "]", true, 0);
		return _comm.coordinator->sendString(cmd);
	}
	else {
		//writeLogLinePlain("Send [" + TFormater::toDisplayable(cmd) + "]", true, 0);
		if (_meta.isSimulated > 0) {
			_callbacks.serialSentCB(true, cmd.c_str(), cmd.length(), nullptr, 0);
			return 1;
		}
		else {
			return _comm.commHandle->sendString(cmd);
		}
	}
}

int GeneralSlave::sendU8(U8Vec& cmd) {
	if (_comm.coordinator != nullptr) {
		writeLogLinePlain("B-Send [" + TFormater::toDisplayable(cmd) + "]", true, 0);
		return _comm.coordinator->sendU8(cmd);
	}
	else {
		//writeLogLinePlain("Send [" + TFormater::toDisplayable(cmd) + "]", true, 0);
		if (_meta.isSimulated > 0) {
			_callbacks.serialSentCB(true, cmd.data(), cmd.size(), nullptr, 0);
			return 1;
		}
		else {
			return _comm.commHandle->sendBytes(cmd);
		}
	}
}

int GeneralSlave::receive(std::string* replyOut, int overwriteTimeout) {
	string reply;
	if (_comm.coordinator != nullptr) {
		// TAutomatonFrameCoordinator::receive
		_comm.coordinator->receive(&reply);
		writeLogLinePlain("B-Received [" + TFormater::toDisplayable(reply) + "]", true, 0);
	}
	else {
		if (_meta.isSimulated > 0) {
			reply = "Simulated";
		}
		else {
			if (overwriteTimeout < 0) {
				overwriteTimeout = _comm.defaultTimeout;
			}
			reply = _comm.commHandle->receiveStrReplyWithDelim(overwriteTimeout, '\n');
		}
		writeLogLinePlain("Received [" + TFormater::toDisplayable(reply) + "]", true, 0);
	}
	_comm.lastReceivedDt = _comm.newReceivedDt;
	_comm.newReceivedDt = reply;
	if (replyOut != nullptr) {
		*replyOut = reply;
	}
	return 1;
}

int GeneralSlave::checkReply(std::string replyExp) {
	if (isSimu()) {
		return 1;
	}
	string reply;
	if (receive(&reply)) {
		return reply.compare(replyExp) == 0;
	}
	return -1;
}

int GeneralSlave::purgeReply() {
	if (_meta.isSimulated > 0) {
	}
	else {
		if (_comm.commHandle != nullptr) {
			_comm.commHandle->purge();
		}
	}
	return 1;
}

void GeneralSlave::writeLogLinePlain(std::wstring line, bool withTime, int padding) {
	_logger.writeLogLineCore(line, withTime, padding);
}

void GeneralSlave::writeLogLinePlain(std::string line, bool withTime, int padding) {
	_logger.writeLogLineCore(TFormater::string2wstring(line), withTime, padding);
}

void GeneralSlave::Logger::writeLogLineCore(std::wstring lineW, bool flagWithTime, int padding) {
	if ((logFilenameRef != nullptr) && (logFilenameRef->length() > 0)) {
		wofstream myfile;
		myfile.open(*logFilenameRef, ios::out | ios::app);
		if (flagWithTime) {
			int timeMS = TStopWatch::getTimeSinceStart();
			wstring timeStr = TFormater::int2wstr(timeMS, 9);
			myfile.write(timeStr.c_str(), 3);
			myfile.write(L",", 1);
			myfile.write(timeStr.c_str() + 3, 3);
			myfile.write(L",", 1);
			myfile.write(timeStr.c_str() + 6, 3);
			myfile.write(L" ", 1);
		}

		for (int idx = 0; idx < padding; idx++) {
			myfile.write(L" ", 1);
		}

		myfile.write(lineW.c_str(), lineW.length());
		myfile.write(L"\n", 1);
		myfile.close();
	}
#ifdef _CONSOLE
	else {
		//#endif
		wcout << lineW << endl;
		//#ifndef _CONSOLE
	}
#endif
}

GeneralSlave::LogBlockCtrl::LogBlockCtrl(LogBlockCtrl&& ref) {
	groupIndentStrRef = ref.groupIndentStrRef;
	blockName = ref.blockName;
	loggerRef = ref.loggerRef;
	flagReleased = false;

	ref.flagReleased = true;
}

GeneralSlave::LogBlockCtrl::LogBlockCtrl(std::wstring* groupIndentStrRefIn, const std::wstring& blockNameIn, Logger* loggerRefIn) {
	groupIndentStrRef = groupIndentStrRefIn;
	blockName = blockNameIn;
	loggerRef = loggerRefIn;

	loggerRef->writeLogLineCore(*groupIndentStrRef + blockName + L" start", true, 0);
	addIndent();
}

GeneralSlave::LogBlockCtrl::~LogBlockCtrl() {
	if (!flagReleased) {
		dropIndent();
		loggerRef->writeLogLineCore(*groupIndentStrRef + blockName + L" end", true, 0);
	}
}

void GeneralSlave::LogBlockCtrl::addIndent() {
	groupIndentStrRef->append(L"-| ");
}

void GeneralSlave::LogBlockCtrl::dropIndent() {
	if (groupIndentStrRef->size() >= 3) {
		groupIndentStrRef->resize(groupIndentStrRef->size() - 3);
	}
}


int GeneralSlave::CommCallback::addMeToArray(GeneralSlave* myPtr) {
	for (int idx = 0; idx < allSlaveReps.size(); idx++) {
		if (allSlaveReps[idx] == nullptr) {
			allSlaveReps[idx] = myPtr;
			return idx;
		}
	}
	allSlaveReps.push_back(myPtr);
	return allSlaveReps.size() - 1;
}

void GeneralSlave::CommCallback::removeMeFromArray(int index) {
	allSlaveReps[index] = nullptr;
}

std::vector<GeneralSlave*>GeneralSlave::CommCallback::allSlaveReps;

SlaveInitializer::SlaveInitializer() {
	genSlaveInit();
	_meta.referenceName = L"_pending";
	setDefaultFriendlyName();
	//_callbacks.sentCB2Master = TAutomaLogs::logSerialSent;
	//_callbacks.receiveCB2Master = TAutomaLogs::logSerialReceive;
	//_callbacks.timeoutCB2Master = TAutomaLogs::logSerialTimeout;

	CommsInterface::pendingPortCBRefIdx = _commCB.callbackIndex;
	CommsInterface::pendingPortSentCB = _callbacks.serialSentCBWrap;
	CommsInterface::pendingPortReceiveCB = _callbacks.serialReceiveCBWrap;
}

SlaveInitializer globalSlaveInitializer;